﻿using System;
using System.Linq;
using System.Collections.Generic;

namespace SpeechRecognition.Domain.Input
{
    public class WaveProcessor : IWaveProcessor
    {
        public double[] CutOffLatentPeriod(short[] sequence)
        {
            double[] result;
            double d1 = Dispersion(sequence);
            double threshold = Math.Sqrt(d1) / 5;
            var sequenceSkippedLeft = sequence.SkipWhile(x => Math.Abs(x) < threshold);
            sequence = sequenceSkippedLeft.Reverse().SkipWhile(x => Math.Abs(x) < threshold).Reverse().ToArray();
            double d2 = Math.Sqrt(Dispersion(sequence));
            result = new double[sequence.Length];
            for (int i = 0; i < sequence.Length; i++)
            {
                result[i] = (sequence[i] / d2);
            }
            return result;
        }

        private double Dispersion(short[] sequence)
        {
            double result = 0.0;
            for (int i = 0; i < sequence.Length; i++)
            {
                result += sequence[i] * sequence[i];
            }
            return result / sequence.Length;
        }

        public List<List<double>> Separate(double[] sequence)
        {
            List<List<double>> resultSequences = new List<List<double>>();
            int stepNumber = 0;
            int lastPos = stepNumber + 220;

            //разобъем последовательность на участки длиной 220. Хочу спать, возможен полный бред в коде
            while (lastPos <= sequence.Length)
            {
                List<double> oneSequence = new List<double>();
                {
                    while (stepNumber < lastPos)
                    {
                        oneSequence.Add(sequence[stepNumber]);
                        stepNumber++;
                    }
                }
                lastPos = stepNumber + 220;
                resultSequences.Add(oneSequence);
            }
            //оставшуюся часть, если она есть, запихнем в последний массив. Длина этой части меньше 220 будет
            List<double> lastSequence = new List<double>();
            while (stepNumber < sequence.Length)
            {
                lastSequence.Add(sequence[stepNumber]);
                stepNumber++;
            }

            resultSequences.Add(lastSequence);
            return resultSequences;
        }

        public List<List<double>> Matie(List<List<double>> sequences)
        {
            List<List<double>> result = new List<List<double>>();

            for (int i = 0; i < sequences.Count; i++)//для каждой последовательности
            {
                List<double> sR = new List<double>();
                

                for (int r = 0; r < 110; r++)
                {
                    double sum = 0.0;
                    for (int j = 0; j < sequences[i].Count; j++)
                    {
                        double z = (double)j / 220.0 * 2.0 * Math.PI;
                        sum += (double)sequences[i][j] * Ce(r, z, 1);
                    }

                    sR.Add(sum);
                }
                result.Add(sR);

            }


            return result;
        }

        private double Ce(double r, double z, double q)
        {
            double result = 0.0;
            if (r == 0)
            {
                result = 1.0 / Math.Sqrt(2) * (1 - q * Math.Cos(2 * z) / 2 + q * q *
                    (Math.Cos(4 * z) / 32 - 1 / 16) - q * q * q * (Math.Cos(6 * z) / 1152 - 11 * Math.Cos
                      (2 * z) / 128));
            }
            else if (r == 1)
            {
                result = Math.Cos(z) - q * Math.Cos(3 * z) / 8 + q * q *
                  (Math.Cos(5 * z) / 192 - Math.Cos(3 * z) / 64 - Math.Cos(z) / 128) - q * q * q *
                  (Math.Cos(7 * z) / 9216 - Math.Cos(5 * z) / 1152 - Math.Cos(3 * z) / 3072 + Math.Cos(z)
                    / 512);
            }
            else if (r == 2)
            {
                result = Math.Cos(2 * z) - q * (Math.Cos(4 * z) / 12 - 1 / 4) + q * q *
                  (Math.Cos(6 * z) / 384 - 19 * Math.Cos(2 * z) / 288);
            }
            else
            {
                result = Math.Cos(r * z) - q * (Math.Cos(r + 2) / (4 * (r + 1)) - Math.Cos(r - 2) /
                    (4 * (r - 1))) + q * q * (Math.Cos(r + 4) / (32 * (r + 1) * (r + 2)) - Math.Cos
                    (r - 4) / (32 * (r - 1) * (r - 2))) - Math.Cos(r * z) / 32 *
                  (2 * (r * r + 1) / Math.Pow(r * r - 1, 2));
            };

            return result;
        }
    }
}
